# -*- conding:utf-8 -*-

"""
@author: Laowang
@contact: QQ:1125564921
@Created on: 2022/6/23
@Remark: 
"""
import operator
from collections import ChainMap
from typing import Dict
from logging import INFO
from queue import Queue, Empty
from typing import Callable, Optional, Any
from dataclasses import dataclass, Field, field
from datetime import datetime


from framework_strategy.gopy.gateways.utils import RequestParamterObj, ResponseEventObj


class AllPropertiesAsDefault:

    """
    ---
    #Parameter:
        * _source : Used to distinguish between action events on different platforms
        *_isasynchronous : This is called synchronously by default
        * __attrs_dict__ : Extract useful parameters
    """

    _fields: Dict = {
        "_res_queue": Queue(),
        "_isasynchronous": False
    }
    _save = ["market"]

    def __init__(self, **kwargs):
        """ Initialize the default values """
        __dict = {}

        ex: str = kwargs["_source"]  # 必传参数
        ex_method = operator.methodcaller(ex.lower() + "_init", kwargs)
        ex_kw: dict = ex_method(self)

        for _k, _v in ChainMap(ex_kw, self.__dataclass_fields__, self._fields).items():
            if _k in self._save and _k in self.__dict__:
                continue
            setattr(self, _k, _v)
            if not _k.startswith("_"):
                __dict[_k] = _v
            self.__attrs_dict__ = __dict

    @property
    def kwargs(self):
        """ Gets the request parameter data """
        dict_ = {}
        for _k, _v in self.__attrs_dict__.items():
            if not isinstance(_v, Field):
                dict_[_k] = _v
        return dict_

    def __setattr__(self, __name: str, __value: Any) -> None:
        self.__dict__[__name] = __value

        if hasattr(self, "__attrs_dict__") and __name in self.__attrs_dict__:
            self.__attrs_dict__[__name] = __value


@dataclass(init=False)
class BaseData(AllPropertiesAsDefault):
    """ 
    Data guarantees serial execution
    Note: A queue is required

    Parameter
    ----
        *_extra : Used for additional parameter passing,
         preserving the context of the internal parameter passing
        *_source : Used to specify the exchange to operate on
        *_extra : You can refer to the DAO to show off the corresponding modle field information
          _extra = field(default_factory=dict)
    """

    _source: Optional[str]
    _extra: Any = field(default_factory=dict)

    def __post__init__(self):
        pass

    def get_result(self, block=True, timeout=20):
        """ waiting until result If you expect results from synchronization, 
        it is recommended to set a timeout to avoid blocking  """
        try:
            res = self._res_queue.get(block=block, timeout=timeout)
        except Empty:
            res = ResponseEventObj(False, None, None, None)
        return res

    def put(self, data: RequestParamterObj):
        """ Put an item into the queue without blocking. 

        Only enqueue the item if a free slot is immediately available. 
        Otherwise raise the Full exception. """
        try:
            self._res_queue.put_nowait(data)
        except Empty:
            pass

    def add_callback(self, handle: Callable[[ResponseEventObj], None]):
        """ event call """
        # TODO
        pass

    @property
    def exchange(self):
        return self._source

    @property
    def extra(self):
        return self._extra

    @property
    def isasynchronous(self):
        return self._isasynchronous

    @isasynchronous.setter
    def isasynchronous(self, _isasynchronous):
        self._isasynchronous = _isasynchronous


def _event(event: str, data: BaseData):
    """
    The different events are specified by the different platform event templates configured in Setting 
    """
    return event.format(data._source), data


@dataclass
class LogData(BaseData):
    """
    Log data is used for recording log messages on GUI or in log files.
    """

    msg: str = ""
    level: int = INFO

    def __post_init__(self):
        """"""
        self.time = datetime.now()
